import os
import time
import pymysql
import requests
import re
from random import randint
from bs4 import BeautifulSoup
from argparse import ArgumentParser
import logging
log_file = 'csdn.log'
os.remove(log_file)
logger = logging.getLogger()
logger.setLevel('DEBUG')
BASIC_FORMAT = "%(asctime)s:%(levelname)s:%(message)s"
DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(BASIC_FORMAT, DATE_FORMAT)
chlr = logging.StreamHandler() # 输出到控制台的handler
chlr.setFormatter(formatter)
chlr.setLevel('DEBUG')  # 也可以不设置，不设置就默认用logger的level
fhlr = logging.FileHandler(log_file) # 输出到文件的handler
fhlr.setFormatter(formatter)
logger.addHandler(chlr)
logger.addHandler(fhlr)

class connect:
    def __init__(self, user='root', host='localhost',
                 port=3306, passwd='1', db_name='csdn', user_name=1):
        self.user = user
        self.host = host
        self.port = port
        self.passwd = passwd
        self.db_name = db_name
        self.user_name = user_name

    def update_data(self, field, field_name, new_data, name, table, key=None, value=None):
        """
        更新数据
        :param field: 需要修改的字段,例如name
        :param field_name: 该字段的备注
        :param key: 指定查询字段
        :param value: 字段匹配值
        :param new_data: 新数据
        :param name: 任务名称
        :param table: 数据表名称
        :return:
        """
        try:
            connect = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.passwd)
            connect.select_db(self.db_name)
        except:
            logger.error('数据库连接失败,请联系作者或者在打开的页面中获取最新版')
            return False
        cmd = connect.cursor()
        try:
            sql = f"UPDATE {self.db_name}.{table} SET {field}='{new_data}' WHERE  {key}='{value}';"
            # print(sql)
            cmd.execute(sql)
            connect.commit()
            cmd.close()
            logger.info(f"为 [ {name} ]更新{field_name}->[ {new_data} ]成功")
        except:
            logger.error(f"为 [ {name} ]更新{field_name}->[ {new_data} ]失败")
            return False

    def get_all(self, table, value=None, key=None):
        """
        查询某个ID下某个字段的内容，以此达到获取对应信息的功能，例如phone可以获取手机号码
        :param table: 数据表名称
        :return: 获取的数据
        """
        try:
            connect = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.passwd)
            connect.select_db(self.db_name)
        except:
            logger.error('数据库连接失败,请联系作者或者在打开的页面中获取最新版')
            return False
        try:
            cmd = connect.cursor()
            sql = f"select * from {table} where {key}='{value}';"
            cmd.execute(sql)
            sum = cmd.fetchall()
            logger.info(f'获取数据库信息: success')
            connect.close()
            if not sum:
                return False
            else:
                return sum
        except:
            logger.error(f'获取数据库信息:  fail')
            connect.close()
            return False

    def get(self, table, name, key=None, value=None):
        """
        查询某个ID下某个字段的内容，以此达到获取对应信息的功能，例如phone可以获取手机号码
        :param name: 字段名称
        :param key: 条件
        :param table: 数据表名称
        :param value: 值
        :return: 获取的数据
        """
        try:
            connect = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.passwd)
            connect.select_db(self.db_name)
        except:
            logger.error('数据库连接失败,请检查登录信息...')
            return False
        try:
            cmd = connect.cursor()
            sql = f"""select {name} from {self.db_name}.{table} where {key}='{value}';"""
            if not key:
                sql = f"""select {name} from {self.db_name}.{table};"""
            cmd.execute(sql)
            sum = cmd.fetchall()
            if not sum:
                connect.close()
                return False
            else:
                connect.close()
                logger.info(f'获取数据库信息: success')
                return sum[0]
        except:
            logger.error(f'获取{name}:  fail')
            connect.close()
            return False
    def get_count(self, table_name):
        """
        查询数据表的总行数
        :param table_name: 数据表的名称
        :return: 行数
        """
        try:
            connect = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.passwd)
            connect.select_db(self.db_name)
        except:
            logger.error('数据库连接失败,请联系作者或者在打开的页面中获取最新版')
            return False
        try:
            cmd = connect.cursor()
            sql = f"explain select * from {self.db_name}.{table_name};"
            logger.info(sql)
            # sql = f"SELECT COUNT(*) FROM {self.db_name}.{table_name};"
            cmd.execute(sql)
            logger.info('获取用户数量: success')
            sum = cmd.fetchall()[0][9]
            return sum
        except:
            logger.error('数据库查询错误,请检查数据表名称')
            return False
def format(txt):
    """
    格式化文本
    :param txt: 文本内容
    :return: 结果
    """
    info = re.sub(r'.*svg.*', '', txt)

    info = re.sub(r'.*svg.*', '', info)
    info = re.sub(r'<path.*', '', info)

    # 转换菜单
    info = re.sub(r'<h1><a id=".*"></a>', '# ', info)
    info = re.sub(r'<h2><a id=".*"></a>', '## ', info)
    info = re.sub(r'<h3><a id=".*"></a>', '### ', info)
    info = re.sub(r'</h.*>', '', info)

    # 转换正文
    info = re.sub(r'<.*p>', '', info)
    info = re.sub(r'<span class="token function">', '', info)
    info = re.sub(r'</span>', '', info)
    info = re.sub(r'</code></pre>', f'```\n', info)
    info = re.sub(r"""<span class="token string">""", ' ', info)
    info = re.sub(r"""<span class="token function">""", '', info)
    info = re.sub(r"""<span class="token keyword">""", '', info)
    info = re.sub(r"""<span class="token operator">""", '', info)
    info = re.sub(r"""<span class="token comment">""", '', info)
    info = re.sub(r"""<pre><code class="prism language-bash">""", '```bash\n', info)
    info = re.sub(r"""<pre><code class="prism language-python">""", '```python\n', info)
    info = re.sub(r"""<span class="token punctuation">""", '', info)
    info = re.sub(r'&gt;', '>', info)
    info = re.sub(r'&lt;', '<', info)
    info = re.sub(r' <tbody>', '', info)
    info = re.sub(r'<tr><td>', '|', info)
    info = re.sub(r'<(.*)table>', '', info)
    info = re.sub(r'<(.*)thead>', '', info)
    info = re.sub(r'</blockquote>', '', info)
    return info

class csdn:
    def __init__(self, user, head):
        """
        初始化请求参数
        :param user: CSDN用户ID
        :param head: 访问请求头
        """
        self.useinfo = user
        self.url = f"https://blog.csdn.net/{user}?type=blog"
        self.head = head
    def write_md(self,file,txt, mode='del'):
        """
        写入文本内容到文件
        :param file: 文件名
        :param txt: 文本内容
        :param mode: 文件已存在如何处理,del删除,skip跳过
        :return: 写入结果
        """
        if not os.path.isdir(path):
            os.mkdir(path)
        file_name = f"{path}/{file}"
        print(f"保存路径: {file_name}")
        if os.path.exists(file_name):
            if str(mode) == 'skip':
                logger.debug(f"文件--> [ {file_name} ] 已存在,本次跳过")
                return True
            else:
                logger.error(f"测试")
                try:
                    with open(file_name, 'w', encoding='utf-8') as f:
                        f.write(txt)
                    logger.info(f"{file_name}写入: 成功")
                except:
                    logger.error(f"{file_name}写入: 失败")
        else:
            try:
                with open(file_name, 'w', encoding='utf-8') as f:
                    f.write(txt)
                logger.info(f"{file_name}写入: 成功")
            except:
                logger.error(f"{file_name}写入: 失败")
    def get_status(self):
        """
        对URL进行访问测试
        :return: 访问结果
        """
        get = requests.get(url=self.url, headers=self.head)
        self.get = get
        if get.status_code == 200:
            logger.info(f'访问请求成功')
            return True
        else:
            logger.error(f"访问请求失败")
            return False
    def get_list(self):
        """
        获取该用户所有文章列表
        :return: 返回列表信息
        """
        self.get.encoding = self.get.apparent_encoding
        soup = BeautifulSoup(self.get.text, features="lxml")
        title_list = soup.find_all('article', 'blog-list-box')
        for i in title_list:
            # 获取URL
            reg = re.findall(rf'href=(.*)"', str(i))[0]
            reg = re.sub(pattern='^"', repl='', string=reg)
            URL = re.sub(pattern=r'\"(.*)', repl='', string=reg)

            # 获取主题
            title_get = str(i).split('=')[12]
            title_get = re.sub(pattern=r"""\"\">""", repl='', string=title_get)
            title = re.sub(pattern=rf"</h4(.*)", repl='', string=title_get)
            up_get.doc(title=title, url=URL)
            # d = input(f"当前模式: {mode}")
            if int(mode) == 2:
                sec = randint(3, 10)
                time.sleep(sec)
                logger.debug(f"本次延时: {sec}")
            print(f"当前主题: {title}\t 当前URL:{URL}")
            input('测试机')

    def doc(self, title='Linux通过chrony进行时间同步', url='https://blog.csdn.net/qq_36154886/article/details/116025999'):
        """
        爬取单篇文章内容
        :param title: 文章标题
        :param url: 地址
        :return: str(文章内容)
        """
        file = f"{title}.md"
        gets = requests.get(url=url, headers=self.head)
        if gets.status_code == 200:
            logger.info(f"结果: [ {title} ]---> 请求成功")
            logger.info(f"访问URL: [ {url} ]")
            gets.encoding = gets.apparent_encoding
            soup = BeautifulSoup(gets.text, 'lxml')
            text_list = soup.find('div', "markdown_views prism-tomorrow-night")
            print(text_list)
            info = ""
            for i in text_list:
                info = f"{str(info)} {i}"
                info = format(txt=info)
            if save:
                status = up_get.write_md(file=file, txt=info)
                logger.info(status)
        else:
            logger.error(f"结果: [ {title} ]---> 请求失败")
            logger.error(f"访问URL: [ {url} ]")
            return False


if __name__ == "__main__":


    db = connect()
    all_info = ArgumentParser(description='设置使用参数')
    all_info.add_argument('-u', '--user', help="设置CSDN用户名(用户ID)",
                          required=True)
    all_info.add_argument('-mode', '--mode', type=int,
                          help="设置运行模式,[1]一触即达 [2]仿真模式", default=1,
                          required=False)
    all_info.add_argument('-pe', '--pe', help="设置使用授权码", default=1, type=int,
                          required=False)
    all_info.add_argument('-ce', '--ce', help="仿真模式授权码", default=1, type=int,
                          required=False)
    all_info.add_argument('-l', '--log', help='设置运行日志文件, 默认: csdn.log', default='csdn.log',
                          type=str, required=False)
    all_info.add_argument('-p', '--path', help='设置保存MD文件的路径(必须自己创建该文件夹),默认: ./get/', default='./get',
                          type=str, required=False)
    all_info.add_argument('-s', '--save', help='设置是否保存MD文件(on/off),默认: off', default='on',
                          type=str, required=False)
    ### 获取参数值
    all = all_info.parse_args()
    users = all.user
    mode = int(all.mode)
    pe = int(all.pe)
    ce = int(all.ce)
    path = all.path
    log_file = all.log
    save = all.save
    
    
    ## 获取授权
    permit = 111
    # permit = db.get(table='user', name='permit', value=users, key='user')[0]
    logger.info(f"[ {users} ]授权剩余天数: {permit}")
    ces = db.get(table='code', name='ce', value=None, key=None)[0]
    pes = db.get(table='code', name='pe', value=None, key=None)[0]
    
    ## 判断是否需要保存MD文件
    if save and str(save) == 'on':
        ## 判断MD文件保存路径是否存在
        if not os.path.isdir(path):
            exit(f"路径不存在: {path}")
        save=True
    else:
        save=False
            
    ## 检测使用授权是否正确        
    if int(pe) == int(pes):
        ## 设置请求头
        Headers = {
            "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "cache-control": "max-age=0",
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36 Edg/90.0.818.39}'}
        
        ## 判断使用模式和模式授权码
        if int(mode) == 2 and int(ce) == int(ces):
            ## 判断使用授权剩余天数
            if int(permit) >= int(1):
                ## 获取cookie信息
                get_cookie = db.get_all(table='cookie', value=1, key='id')[0]
                
                ## 设置云端cookie参数
                Headers = {"accept": get_cookie[1],
                           "accept-encoding": get_cookie[2],
                           "accept-language": get_cookie[3],
                           "cache-control": get_cookie[4],
                           'user-agent': get_cookie[5]}
            else:
                logging.error(f"用户: {users} 授权已过期,将使用普通模式")
                d = input('d')
                mode = 1
        else:
            mode = 1
        #  开始实例化
        up_get = csdn(user=users, head=Headers)
        get_status = up_get.get_status()  # 获取请求状态
        if get_status:
            logger.info('开始获取文章列表')
            up_get.get_list()
        else:
            logger.error('请检查用户ID是否有误或者网络是否通畅')
    else:
        logger.error(f'使用授权码错误')